import { isBoolean, isFunction, isArray } from '@/utils/is';
import { buildUUID } from '@/utils/uuid';
import { cloneDeep, get, merge } from 'lodash-es';
import { Ref, ComputedRef, reactive, watchEffect } from 'vue';
import { ref, unref, watch, onMounted, computed } from 'vue';
import { FETCH_FIELD_SETTING, ROW_KEY } from '../constant';
import { PaginationProps } from '../types/pagination';
import { BasicTableProps, FetchParams, SorterResult } from '../types/table';

interface ActionType {
	setLoading: (loading: boolean) => void;
	getPaginationRef: ComputedRef<PaginationProps | boolean>;
	setPagination: (info: Partial<PaginationProps>) => void;
	getFieldsValue: () => Recordable;
	clearSelectedRowKeys: () => void;
	tableData: Ref<Recordable[]>;
	getAutoCreateKey: ComputedRef<boolean>;
	getRowKey: ComputedRef<string | ((record: Recordable<any>) => string)>;
}

interface TableSearchInfo {
	filterInfo: Record<string, any[]>;
	sorterInfo: Recordable;
}

export function useDataSource(
	propsRef: Ref<BasicTableProps>,
	{
		tableData,
		setLoading,
		getPaginationRef,
		setPagination,
		getFieldsValue,
		clearSelectedRowKeys,
		getAutoCreateKey,
		getRowKey,
	}: ActionType,
	emit: EmitType,
	filteredInfo: Ref<Recordable | undefined>,
) {
	const dataSourceRef = ref<Recordable[]>([]);
	const rawDataSourceRef = ref<Recordable>({});
	const tableSearchInfo = reactive<TableSearchInfo>({
		filterInfo: {},
		sorterInfo: {},
	});

	watchEffect(() => {
		tableData.value = unref(dataSourceRef);
	});

	watch(
		() => unref(propsRef).dataSource,
		() => {
			const { api, dataSource } = unref(propsRef);
			if (!api && dataSource) {
				dataSourceRef.value = dataSource;
			}
		},
		{
			immediate: true,
		},
	);

	function setTableKey(items: any[]) {
		if (!items || !Array.isArray(items)) return;
		items.forEach(item => {
			if (!item[ROW_KEY]) {
				item[ROW_KEY] = buildUUID();
			}
			if (item.children && item.children.length) {
				setTableKey(item.children);
			}
		});
	}

	const getDataSourceRef = computed(() => {
		const dataSource = unref(dataSourceRef);
		if (!dataSource || dataSource.length === 0) {
			return [];
		}
		// 若不提供rowKey,自动创建
		if (unref(getAutoCreateKey)) {
			const firstItem = dataSource[0];
			const lastItem = dataSource[dataSource.length - 1];

			if (firstItem && lastItem) {
				if (!firstItem[ROW_KEY] || !lastItem[ROW_KEY]) {
					const data = cloneDeep(unref(dataSourceRef));
					data.forEach(item => {
						if (!item[ROW_KEY]) {
							item[ROW_KEY] = buildUUID();
						}
						if (item.children && item.children.length) {
							setTableKey(item.children);
						}
					});
					dataSourceRef.value = data;
				}
			}
		}
		setLoading(false);
		return unref(dataSourceRef);
	});

	function handleTableChange(
		pagination: PaginationProps,
		filters: Partial<Recordable<string[]>>,
		sorter: SorterResult,
	) {
		const { clearSelectOnPageChange, filterFn, sorterFn } = unref(propsRef);
		if (clearSelectOnPageChange) {
			clearSelectedRowKeys();
		}
		setPagination(pagination);

		const param: Recordable = {};
		if (sorter && isFunction(sorterFn)) {
			const sortInfo = sorterFn(sorter);
			tableSearchInfo.sorterInfo = sortInfo;
			param.sortInfo = sortInfo;
		}

		if (filters && isFunction(filterFn)) {
			filteredInfo.value = filters;
			const filterRs = filterFn(filters);
			tableSearchInfo.filterInfo = filterRs;
			param.filterInfo = filterRs;
		}
		fetch(param);
	}

	// 更新表格的某一项 TODO: 发送请求
	function updateTableData(index: number, key: string, value: any) {
		const record = dataSourceRef.value[index];
		if (record) {
			dataSourceRef.value[index][key] = value;
		}
		return dataSourceRef.value[index];
	}

	function updateTableDataRecord(rowKey: string | number, record: Recordable): Recordable | undefined {
		const row = findTableDataRecord(rowKey);

		if (row) {
			for (const field in row) {
				if (Reflect.has(record, field)) row[field] = record[field];
			}
			return row;
		}
	}

	function deleteTableDataRecord(rowKey: string | number | string[] | number[]) {
		if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
		const rowKeyName = unref(getRowKey);
		if (!rowKeyName) return;
		const rowKeys = !Array.isArray(rowKey) ? [rowKey] : rowKey;
		for (const key of rowKeys) {
			let index: number | undefined = dataSourceRef.value.findIndex(row => {
				let targetKeyName: string;
				if (typeof rowKeyName === 'function') {
					targetKeyName = rowKeyName(row);
				} else {
					targetKeyName = rowKeyName as string;
				}
				return row[targetKeyName] === key;
			});
			if (index >= 0) {
				dataSourceRef.value.splice(index, 1);
			}
			index = unref(propsRef).dataSource?.findIndex(row => {
				let targetKeyName: string;
				if (typeof rowKeyName === 'function') {
					targetKeyName = rowKeyName(row);
				} else {
					targetKeyName = rowKeyName as string;
				}
				return row[targetKeyName] === key;
			});
			if (typeof index !== 'undefined' && index !== -1) unref(propsRef).dataSource?.splice(index, 1);
		}
		setPagination({
			total: unref(propsRef).dataSource?.length,
		});
	}

	function insertTableDataRecord(record: Recordable, index: number): Recordable | undefined {
		if (unref(getAutoCreateKey) && !record[ROW_KEY]) {
			record[ROW_KEY] = buildUUID();
		}
		index = index ?? dataSourceRef.value?.length;
		unref(dataSourceRef).splice(index, 0, record);
		return unref(dataSourceRef);
	}

	function findTableDataRecord(rowKey: string | number) {
		if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;

		const rowKeyName = unref(getRowKey);
		if (!rowKeyName) return;

		const { childrenColumnName = 'children' } = unref(propsRef);

		const findRow = (array: any[]) => {
			let ret;
			array.some(function iter(r) {
				if (typeof rowKeyName === 'function') {
					if ((rowKeyName(r) as string) === rowKey) {
						ret = r;
						return true;
					}
				} else {
					if (Reflect.has(r, rowKeyName) && r[rowKeyName] === rowKey) {
						ret = r;
						return true;
					}
				}
				return r[childrenColumnName] && r[childrenColumnName].some(iter);
			});
			return ret;
		};

		return findRow(dataSourceRef.value);
	}

	/**
	 * 请求接口
	 * 目前不是keywordParam 模式，该部分已被注释
	 */
	async function fetch(opts?: FetchParams): Promise<any> {
		const {
			api,
			searchInfo,
			keyWordInfo,
			loading,
			pagination,
			fetchFieldsSetting,
			beforeFetch,
			afterFetch,
			useSearchForm,
		} = unref(propsRef);

		if (!api && !isFunction(api)) return;
		try {
			loading && setLoading(loading);

			const { pageField, pageSizeField, listField, totalField, isShrq } = Object.assign(
				{},
				FETCH_FIELD_SETTING,
				fetchFieldsSetting,
			);

			const { filterInfo, sorterInfo = {} } = tableSearchInfo;

			// 分页信息的默认值，usePagination
			let pageParams: Recordable = {};
			const getPagination = unref(getPaginationRef);
			let current = 1;
			let pageSize = 10;

			if ((isBoolean(pagination) && !pagination) || isBoolean(getPagination)) {
				pageParams = {};
			} else {
				current = getPagination.current || 1;
				pageSize = getPagination.pageSize || pageSize;
				// 字段的自定义与默认值，
				pageParams[pageField] = opts?.page || current;
				pageParams[pageSizeField] = pageSize;
			}

			let params: Recordable = {};

			if (isShrq) {
				params = merge(
					searchInfo,
					opts?.searchInfo ?? {},
					// opts?.sorterInfo ?? {},
					// opts?.filterInfo ?? {},
				);
				params.page = pageParams;
				params.keyWordParam = merge(
					useSearchForm ? getFieldsValue() : {},
					filterInfo,
					sorterInfo,
					keyWordInfo,
					opts?.filterInfo ?? {},
					opts?.sorterInfo ?? {},
					opts?.keyWordInfo ?? {},
				);
			} else {
				params = merge(
					pageParams,
					useSearchForm ? getFieldsValue() : {},
					searchInfo,
					opts?.searchInfo ?? {},
					sorterInfo,
					filterInfo,
					opts?.sorterInfo ?? {},
					opts?.filterInfo ?? {},
				);
			}

			if (beforeFetch && isFunction(beforeFetch)) {
				params = (await beforeFetch(params)) || params;
			}

			const rs = await api(params);
			rawDataSourceRef.value = rs;
			const isArrayRs = isArray(rs);

			let rsList: Recordable[] = isArrayRs ? rs : get(rs, listField);
			const rsTotal: number = isArrayRs ? rs.length : get(rs, totalField);

			dataSourceRef.value = rsList;

			// 修改并设置正确的分页
			if (rsTotal) {
				const currentTotalPage = Math.ceil(rsTotal / pageSize);
				if (current > currentTotalPage) {
					setPagination({
						current: currentTotalPage,
					});
					return await fetch(opts);
				}
			}

			if (afterFetch && isFunction(afterFetch)) {
				rsList = (await afterFetch(rsList)) || rsList;
			}

			setPagination({ total: rsTotal || 0 });
			if (opts && opts.page) {
				setPagination({ current: opts.page || 1 });
			}

			emit('fetch-success', {
				list: unref(rsList),
				total: rsTotal,
			});
			return rsList;
		} catch (error) {
			emit('fetch-error', error);
			dataSourceRef.value = [];
			setPagination({ total: 0 });
		} finally {
			setLoading(false);
		}
	}

	function setDataSource(values: Recordable[]) {
		dataSourceRef.value = values;
	}

	function getDataSource<T = Recordable>() {
		return unref(getDataSourceRef) as T[];
	}

	function getRawDataSource<T = Recordable>() {
		return rawDataSourceRef.value as T;
	}

	function getFilterInfo() {
		return tableSearchInfo.filterInfo;
	}

	async function reload(opts?: FetchParams) {
		return await fetch(opts);
	}

	onMounted(() => {
		unref(propsRef).immediate && fetch();
	});

	return {
		getDataSourceRef,
		getDataSource,
		getRawDataSource,
		getFilterInfo,
		setDataSource,
		fetch,
		reload,
		handleTableChange,
		updateTableData,
		updateTableDataRecord,
		deleteTableDataRecord,
		insertTableDataRecord,
	};
}
